/**
 * Originally grabbed from the official RaphaelJS Documentation
 * http://raphaeljs.com/graffle.html
 * Adopted (arrows) and commented by Philipp Strathausen http://blog.ameisenbar.de
 * Licenced under the MIT licence.
 */

/**
 * Usage:
 * connect two shapes
 * parameters: 
 *      source shape [or connection for redrawing], 
 *      target shape,
 *      style with { fg : linecolor, bg : background color, directed: boolean }
 * returns:
 *      connection { draw = function() }
 */
Raphael.fn.connection = function(obj1, obj2, style) {
    var selfRef = this;
    /* create and return new connection */
    var edge = {/*
     from : obj1,
     to : obj2,
     style : style,*/
        draw: function() {
            /* get bounding boxes of target and source */
            var bb1 = obj1.getBBox();
            var bb2 = obj2.getBBox();
            var off1 = 0;
            var off2 = 0;
            /* coordinates for potential connection coordinates from/to the objects */
            var p = [
                {x: bb1.x + bb1.width / 2, y: bb1.y - off1}, /* NORTH 1 */
                {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */
                {x: bb1.x - off1, y: bb1.y + bb1.height / 2}, /* WEST  1 */
                {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST  1 */
                {x: bb2.x + bb2.width / 2, y: bb2.y - off2}, /* NORTH 2 */
                {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */
                {x: bb2.x - off2, y: bb2.y + bb2.height / 2}, /* WEST  2 */
                {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2}  /* EAST  2 */
            ];

            /* distances between objects and according coordinates connection */
            var d = {}, dis = [];

            /*
             * find out the best connection coordinates by trying all possible ways
             */
            /* loop the first object's connection coordinates */
            for (var i = 0; i < 4; i++) {
                /* loop the seond object's connection coordinates */
                for (var j = 4; j < 8; j++) {
                    var dx = Math.abs(p[i].x - p[j].x),
                            dy = Math.abs(p[i].y - p[j].y);
                    if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
                        dis.push(dx + dy);
                        d[dis[dis.length - 1].toFixed(3)] = [i, j];
                    }
                }
            }
            var res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis).toFixed(3)];
            /* bezier path */
            var x1 = p[res[0]].x,
                    y1 = p[res[0]].y,
                    x4 = p[res[1]].x,
                    y4 = p[res[1]].y,
                    dx = Math.max(Math.abs(x1 - x4) / 2, 10),
                    dy = Math.max(Math.abs(y1 - y4) / 2, 10),
                    x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
                    y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
                    x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
                    y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
            /* assemble path and arrow */
            var path = ["M", x1.toFixed(3), y1.toFixed(3), "L", x4.toFixed(3), y4.toFixed(3)].join(",");
            var mx, my, tmpx, tmpy;
            /* arrow */
            if (style && style.directed) {
                /* magnitude, length of the last path vector */
                var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3));
                /* vector normalisation to specified length  */
                var norm = function(x, l) {
                    return (-x * (l || 30) / mag);
                };
                /* calculate array coordinates (two lines orthogonal to the path vector) */
                var arr = [
                    {x: (norm(x4 - x3) + norm(y4 - y3) + x4).toFixed(3), y: (norm(y4 - y3) + norm(x4 - x3) + y4).toFixed(3)},
                    {x: (norm(x4 - x3) - norm(y4 - y3) + x4).toFixed(3), y: (norm(y4 - y3) - norm(x4 - x3) + y4).toFixed(3)}
                ];


                var findLine = function(x1, y1, x2, y2) {

                    var line = new Array();

                    var a = y2 - y1;
                    var b = x1 - x2;
                    var c = x2 * y1 - x1 * y2;

                    line[ 0 ] = a;
                    line[ 1 ] = b;
                    line[ 2 ] = c;

                    return line;

                };

                var changeSignForSignOfSecond = function(first, second) {
                    return (second >= 0) ? Math.abs(first) : (-1) * Math.abs(first);
                };

                var resizeVector = function(vector, length) {

                    var x = vector[ 0 ];
                    var y = vector[ 1 ];

                    if (x == 0 && y == 0) {
                        return vector;
                    }

                    if (x == 0) {
                        vector[ 1 ] = changeSignForSignOfSecond(length, y);
                        return vector;
                    }

                    if (y == 0) {
                        vector[ 0 ] = changeSignForSignOfSecond(length, x);
                        return vector;
                    }

                    var ratio = x / y;

                    var xNew;
                    var yNew;

                    xNew = Math.sqrt(length * length * ratio * ratio / (ratio * ratio + 1));
                    xNew = changeSignForSignOfSecond(xNew, x);

                    yNew = xNew / ratio;

                    vector[ 0 ] = xNew;
                    vector[ 1 ] = yNew;

                    return vector;

                };



                var startx = x1;
                var starty = y1;
                var endx = x4;
                var endy = y4;

                var line = findLine(startx, starty, endx, endy);

                var vector = [line[ 0 ], line[ 1 ]];
                vector = resizeVector(vector, 10);

                var toStartVector = [startx - endx, starty - endy];
                toStartVector = resizeVector(toStartVector, 20);


                mx = (x1 + x4) / 2;
                my = (y1 + y4) / 2;
                tmpx = (mx + x4) / 2;
                tmpy = (my + y4) / 2;
                var ahx = (tmpx + x4) / 2;
                var ahy = (tmpy + y4) / 2;
//                var xxx = (tmpx + ahx) / 2;
//                var yyy = (tmpy + ahy) / 2;
                var xxx = ahx + toStartVector[ 0 ];
                var yyy = ahy + toStartVector[ 1 ];

                var x5 = xxx + vector[ 0 ];
                var y5 = yyy + vector[ 1 ];
                var x6 = xxx - vector[ 0 ];
                var y6 = yyy - vector[ 1 ];

                path = path + ",M" + x5 + "," + y5 + ",L" + ahx + "," + ahy + ",L" + x6 + "," + y6;













//                path = path + ",M" + arr[0].x + "," + arr[0].y + ",L" + x4 + "," + y4 + ",L" + arr[1].x + "," + arr[1].y;
            }
            mx = (x1 + x4) / 2;
            my = (y1 + y4) / 2;
            tmpx = (mx + x4) / 2;
            tmpy = (my + y4) / 2;
            /* function to be used for moving existent path(s), e.g. animate() or attr() */
            var move = "attr";
            /* applying path(s) */
            edge.fg && edge.fg[move]({path: path})
                    || (edge.fg = selfRef.path(path).attr({stroke: style && style.stroke || "#000", fill: "none"}).toBack());
            edge.bg && edge.bg[move]({path: path})
                    || style && style.fill && (edge.bg = style.fill.split && selfRef.path(path).attr({stroke: style.fill.split("|")[0], fill: "none", "stroke-width": style.fill.split("|")[1] || 3}).toBack());
            /* setting label */
            style && style.label
                    && (edge.label && edge.label.attr({x: tmpx, y: tmpy})
                            || (edge.label = selfRef.text(tmpx, tmpy, style.label).attr({fill: "#000", "font-size": style["font-size"] || "12px"})));
            style && style.label && style["label-style"] && edge.label && edge.label.attr(style["label-style"]);
            style && style.callback && style.callback(edge);
            edge.fg.attr({'stroke-width': 5});
            edge.fg.mousedown(edgeClick);
            edge.fg.source = obj1[0].node.id;
            edge.fg.target = obj2[0].node.id;
        }
    }
    edge.draw();
    return edge;
};
//Raphael.prototype.set.prototype.dodo=function(){console.log("works");};
